package cn.sourcespro.shiro.security;

import cn.sourcespro.shiro.entity.User;
import cn.sourcespro.shiro.service.PermService;
import cn.sourcespro.shiro.service.RoleService;
import cn.sourcespro.shiro.service.UserService;
import cn.sourcespro.shiro.util.ShiroUtil;
import com.google.common.collect.Lists;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * security
 *
 * @author zhanghaowei
 * @date 2018/7/20
 */
public class HmacRealm extends AuthorizingRealm {

    private static final Logger logger = LoggerFactory.getLogger(HmacRealm.class);

    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private PermService permService;

    /**
     * 授权
     * @param principals
     *      用户标识
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String clientKey = (String) principals.getPrimaryPrincipal();
        User user = userService.findByUsername(clientKey);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setRoles(user.getRoles());
        info.setStringPermissions(user.getPerms());
        logger.info("加载[{}]的角色权限", user.getUsername());
        return info;
    }

    /**
     * 认证
     * @param token
     *      Hmac token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        HmacToken hmacToken = (HmacToken) token;

        //验证数字摘要
        List<String> keys = Lists.newArrayList();
        Map<String, String[]> parameters = hmacToken.getParameters();
        for (String param : parameters.keySet()) {
            if (!"digest".equals(param)) {
                keys.add(param);
            }
        }
        Collections.sort(keys);
        StringBuilder baseString = new StringBuilder();
        for (String key : keys) {
            baseString.append(parameters.get(key)[0]);
        }
        //认证端生成摘要
        String serverDigest = ShiroUtil.hmacDigest(baseString.toString());
        //客户端请求的摘要和服务端生成的摘要不同
        if (!serverDigest.equals(hmacToken.getDigest())) {
            throw new AuthenticationException("数字摘要验证失败!!!");
        }
        Long visitTimeStamp = Long.valueOf(hmacToken.getTimeStamp());
        Long nowTimeStamp = System.currentTimeMillis();
        Long jge = nowTimeStamp - visitTimeStamp;
        //十分钟之前的时间戳，这是有效期可以双方约定由参数传过来
        if (jge > 600000) {
            throw new AuthenticationException("数字摘要失效!!!");
        }
        // 此处可以添加查询数据库检查账号是否存在、是否被锁定、是否被禁用等等逻辑
        return new SimpleAuthenticationInfo(hmacToken.getClientKey(),Boolean.TRUE,getName());
    }

}
